package main

import "fmt"

type Animal struct {
	name string
	age  int
}

type Runner interface {
	run()
}

func (a Animal) run() {
	fmt.Printf("动物 跑, Type=%T %[1]v\n", a)
}

func (a *Animal) print() string {
	return fmt.Sprintf("%s %s", a.name, a.age)
}

type Cat struct {
	Animal        // 父 parent
	color  string // 自我特征
}

func (c Cat) run() {
	// 锦上添花，首先 要使用父方法打底
	// c.run()
	// c.Animal.run()
	fmt.Printf("猫 跑,type=%T %[1]v\n", c)
}

func (*Cat) leap() {
	fmt.Println("猫 跳")
}

func (c *Cat) print() string {
	// return fmt.Sprintf("%s, %d, %s", c.name, c.age, c.color)
	return fmt.Sprintf("%s, %s", c.Animal.print(), c.color)
}

type Dog struct {
	Animal
}

func (Dog) run() {
	fmt.Println("狗 跑")
}

func foo(t Runner) {
	t.run()
}

type Bar struct{}

func (Bar) run() {
	fmt.Println("随便跑")
}

func main() {
	c := Cat{}
	c.run()
	d := Dog{}
	d.run()
	// 一个变量，它可以接受c、d，用这个变量调用 run，会根据这个变量的实际类型调用 run
	// 其他面向对象语言就是这么设计
	// var a Animal = c // 类似写法在 Java 可以的，Go 不允许,使用接口
	// a.run()
	// a = d
	// a.run()

	// var t Runner = c
	// t.run()
	// t = d
	// d.run()
	foo(c) // var t Animal = c 传入的是 Animal 类型的值，c 是 Cat 类型，d 是 Dog 类型
	foo(d)
	foo(Bar{}) // 传入 Bar 类型的值，Bar 实现了 Runner 接口
	// foo(Animal{}) // Animal 没有实现 Runner 接口

}
